package cn.example.util.toolkit;

import cn.example.util.ToolFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.*;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * httpRequest工具类
 *
 * @author Administrator
 */
public class HttpUtil {
    private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);

    public static final String HTTP_HEADER_Accept = "Accept";
    public static final String HTTP_HEADER_ContentType = "Content-Type";

    private static class SingletonHolder {
        private static final HttpUtil INSTANCE = new HttpUtil();
    }

    private HttpUtil() {
    }

    public static HttpUtil getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 获得项目访问路径
     *
     * @param request HttpServletRequest
     * @return String
     */
    public String getProjectHost(HttpServletRequest request) {
        return getServerHost(request) + request.getContextPath();
    }

    /**
     * 获得服务器访问路径
     *
     * @param request HttpServletRequest
     * @return String
     */
    public String getServerHost(HttpServletRequest request) {
        String root = request.getScheme() + "://" + request.getServerName();
        return request.getServerPort() == 80 ? root : root + ":" + request.getServerPort();
    }

    /**
     * 获得客户端IP地址
     *
     * @param request HttpServletRequest
     * @return String
     */
    public String getClientIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip;
    }

    /**
     * 发起HTTP GET请求获取返回结果
     *
     * @param url   请求地址
     * @param heads 请求头
     * @return String
     */
    public String httpGet(String url, Map<String, String> heads) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();

            connection.setDoOutput(false);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod("GET");
            // 设置请求头
            if (heads != null && !heads.isEmpty()) {
                createRequestHeader(connection, heads);
            }
            connection.connect();

            // 将返回的输入流转换成字符串
            InputStream inputStream = connection.getInputStream();
            String result = ToolFactory.getIOUtil().inputStream2Str(inputStream);
            // 释放资源
            inputStream.close();
            connection.disconnect();
            return result;
        } catch (IOException e) {
            log.error("HTTP GET ERROR:", e);
            return null;
        }
    }

    /**
     * 发起HTTP POST请求获取返回结果
     *
     * @param url   请求地址
     * @param str   提交数据
     * @param heads 请求头
     * @return String
     */
    public String httpPost(String url, String str, Map<String, String> heads) {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            connection.setRequestMethod("POST"); // 设置请求方式
            // 设置请求头
            if (heads != null && !heads.isEmpty()) {
                createRequestHeader(connection, heads);
            }
            connection.connect();
            OutputStreamWriter out = new OutputStreamWriter(
                    connection.getOutputStream(), "UTF-8"); // utf-8编码
            out.append(str);
            out.flush();
            out.close();
            // 读取响应
            // 将返回的输入流转换成字符串
            InputStream inputStream = connection.getInputStream();
            String result = ToolFactory.getIOUtil().inputStream2Str(inputStream);
            // 释放资源
            inputStream.close();
            connection.disconnect();
            return result;
        } catch (IOException e) {
            log.error("HTTP POST ERROR:", e);
            return null; // 自定义错误信息
        }
    }

    /**
     * 发起HTTPS GET请求获取返回结果
     *
     * @param url   请求地址
     * @param heads 请求头
     * @return String
     */
    public String httpsGet(String url, Map<String, String> heads) {
        return httpsRequest(url, "GET", null, heads);
    }

    /**
     * 发起HTTPS POST请求获取返回结果
     *
     * @param url   请求地址
     * @param str   提交数据
     * @param heads 请求头
     * @return String
     */
    public String httpsPost(String url, String str, Map<String, String> heads) {
        return httpsRequest(url, "POST", str, heads);
    }

    /**
     * 使用PKCS12（p12）证书发起HTTPS GET请求获取返回结果
     *
     * @param url     请求地址
     * @param p12Path PKCS12（p12）证书路径
     * @param p12Pwd  PKCS12（p12）证书路径密码
     * @param heads   请求头
     * @return String
     */
    public String httpsGet(String url, String p12Path, String p12Pwd, Map<String, String> heads) {
        return httpsRequest(url, "GET", null, p12Path, p12Pwd, heads);
    }

    /**
     * 使用PKCS12（p12）证书发起HTTPS POST请求获取返回结果
     *
     * @param url     请求地址
     * @param str     提交数据
     * @param p12Path PKCS12（p12）证书路径
     * @param p12Pwd  PKCS12（p12）证书路径密码
     * @param heads   请求头
     * @return String
     */
    public String httpsPost(String url, String str, String p12Path, String p12Pwd, Map<String, String> heads) {
        return httpsRequest(url, "POST", str, p12Path, p12Pwd, heads);
    }

    /**
     * 发送https请求
     *
     * @param requestUrl    请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr     提交的数据
     * @param heads         请求头
     * @return String
     */
    private String httpsRequest(String requestUrl, String requestMethod, String outputStr, Map<String, String> heads) {
        try {
            // 创建SSLContext对象，并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection httpsUrlConn = (HttpsURLConnection) url
                    .openConnection();
            httpsUrlConn.setSSLSocketFactory(ssf);

            httpsUrlConn.setDoOutput(true);
            httpsUrlConn.setDoInput(true);
            httpsUrlConn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            httpsUrlConn.setRequestMethod(requestMethod);
            // 设置请求头
            if (heads != null && !heads.isEmpty()) {
                createRequestHeader(httpsUrlConn, heads);
            }

            if ("GET".equalsIgnoreCase(requestMethod)) {
                httpsUrlConn.connect();
            }
            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpsUrlConn.getOutputStream();
                // 注意编码格式，防止中文乱码
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 将返回的输入流转换成字符串
            InputStream inputStream = httpsUrlConn.getInputStream();
            String result = ToolFactory.getIOUtil().inputStream2Str(inputStream);
            // 释放资源
            inputStream.close();
            httpsUrlConn.disconnect();

            return result;
        } catch (IOException | GeneralSecurityException e) {
            log.error("HTTPS " + requestMethod + " ERROR:", e);
            return null;
        }
    }

    /**
     * 发送https请求(使用PKCS12证书)
     *
     * @param requestUrl    请求地址
     * @param requestMethod 请求方式（GET、POST）
     * @param outputStr     提交的数据
     * @param p12Path       PKCS12（p12）证书路径
     * @param p12Pwd        PKCS12（p12）证书路径密码
     * @param heads         请求头
     * @return String
     */
    private String httpsRequest(String requestUrl, String requestMethod, String outputStr, String p12Path, String p12Pwd, Map<String, String> heads) {
        try {
            ResourceLoader resourceLoader = new DefaultResourceLoader();
            KeyStore ks = KeyStore.getInstance("PKCS12");
            char[] password = p12Pwd.toCharArray();
            Resource resource = resourceLoader.getResource("file:" + p12Path);
            ks.load(resource.getInputStream(), password);
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, password);
            SSLContext ssl = SSLContext.getInstance("TLSv1");
            ssl.init(kmf.getKeyManagers(), null, null);
            HttpsURLConnection httpsUrlConn = (HttpsURLConnection) new URL(requestUrl).openConnection();
            httpsUrlConn.setSSLSocketFactory(ssl.getSocketFactory());
            httpsUrlConn.setDoOutput(true);
            httpsUrlConn.setDoInput(true);
            httpsUrlConn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            httpsUrlConn.setRequestMethod(requestMethod);
            // 设置请求头
            if (heads != null && !heads.isEmpty()) {
                createRequestHeader(httpsUrlConn, heads);
            }

            if ("GET".equalsIgnoreCase(requestMethod)) {
                httpsUrlConn.connect();
            }
            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpsUrlConn.getOutputStream();
                // 注意编码格式，防止中文乱码
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }
            // 将返回的输入流转换成字符串
            InputStream inputStream = httpsUrlConn.getInputStream();
            String result = ToolFactory.getIOUtil().inputStream2Str(inputStream);
            // 释放资源
            inputStream.close();
            httpsUrlConn.disconnect();

            return result;
        } catch (IOException | GeneralSecurityException e) {
            log.error("HTTPS PKCS12 " + requestMethod + " ERROR:", e);
            return null;
        }
    }

    /**
     * 设置http请求头
     *
     * @param httpUrlConn HTTP连接对象
     * @param heads       请求头Map
     */
    private void createRequestHeader(URLConnection httpUrlConn, Map<String, String> heads) {
        NullUtil nullUtil = ToolFactory.getNullUtil();
        for (String key : heads.keySet()) {
            if (!nullUtil.isNull(key) && !nullUtil.isNull((heads.get(key)))) {
                httpUrlConn.setRequestProperty(key, heads.get(key));
            }
        }
    }

    /**
     * 按照参数名排序得到参数列表字符串
     *
     * @param params 参数集合对象
     * @return String
     */
    public String createRequestParams(Map<String, String> params) {
        StringBuilder paramStr = new StringBuilder();
        List<String> paramNames = new ArrayList<>(params.size());
        paramNames.addAll(params.keySet());
        Collections.sort(paramNames);
        for (String paramName : paramNames) {
            String temp = paramName + "=" + urlEncodeUTF8(params.get(paramName));
            if (paramStr.toString().equals("")) {
                paramStr.append("?").append(temp);
            } else {
                paramStr.append("&").append(temp);
            }
        }
        return paramStr.toString();
    }

    /**
     * URL编码（utf-8）
     *
     * @param source 原字符串
     * @return String
     */
    public String urlEncodeUTF8(String source) {
        try {
            return URLEncoder.encode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            log.error("URL ENCODE ERROR:", e);
            return source;
        }
    }

    /**
     * URL解码（utf-8）
     *
     * @param source 原字符串
     * @return String
     */
    public String urlDecodeUTF8(String source) {
        try {
            return URLDecoder.decode(source, "utf-8");
        } catch (UnsupportedEncodingException e) {
            log.error("URL DECODE ERROR:", e);
            return source;
        }
    }

    /**
     * 证书信任管理器（用于https请求）
     */
    private class MyX509TrustManager implements X509TrustManager {
        //检查客户端证书
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        //检查服务器端证书
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        //返回受信任的x509数组
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

}
